#include "pyvim.h"


#define CURWIN			(SCR_CURWIN(screen))
#define CURBUF			(SCR_CURBUF(screen))
#define CURMODE			(WIN_MODE(CURWIN))
#define CHAR_WIDTH		(SCR_CHAR_WIDTH(screen))
#define CHAR_HEIGHT		(SCR_CHAR_HEIGHT(screen))

# define TEXT_X(col)	((col) * CHAR_WIDTH)
# define TEXT_Y(row)	((row) * CHAR_HEIGHT + CHAR_ASCENT)
# define FILL_X(col)	((col) * CHAR_WIDTH)
# define FILL_Y(row)	((row) * CHAR_HEIGHT)
# define X_2_COL(x)	((x) / CHAR_WIDTH)
# define Y_2_ROW(y)	((y) / CHAR_HEIGHT)

PyObject		*screen = NULL;

extern long			Rows	= 25L;
extern long			Columns	= 80L;

extern typebuf_T	typebuf = {NULL, 0};

static wchar_t cmdbuf[256];
static int	cmdpos = 0;

static HINSTANCE	s_hinst = NULL;
static HWND			s_hwnd	= NULL;
static HDC			s_hdc	= NULL;
static HBRUSH		s_brush	= NULL;
static HWND			s_textArea = NULL;
static GuiFont		s_font	= 0;

static guicolor_T	s_backcolor = RGB(0, 0, 0);
static guicolor_T	s_forecolor = RGB(255, 255, 255);
static guicolor_T	s_cursorcolor = RGB(255, 255, 0);

static PyObject *gui_redraw(int row1, int col1, int row2, int col2);
static PyObject *gui_mch_draw_string(long row, long col, wchar_t *text, int len, 
		guicolor_T fgcolor, guicolor_T bgcolor);
static void	push_input(int modifiers, int code);

PyDoc_STRVAR(pyvim_doc,
"pyvim module\n");

static const LOGFONT s_lfDefault =
{
    -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
    OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
    PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE,
    "Fixedsys"
};

static GuiFont
get_font_handle(LOGFONT *lf)
{
	HFONT	font = NULL;

	font = CreateFontIndirect(lf);

	if (font == NULL)
		return NOFONT;

	return (GuiFont)font;
}

static void
get_font_size(GuiFont font)
{
	HWND	hwnd = GetDesktopWindow();
	HDC		hdc = GetWindowDC(hwnd);
	HFONT	old = SelectFont(hdc, (HFONT)font);
	TEXTMETRIC	tm;

	GetTextMetrics(hdc, &tm);
	CHAR_WIDTH = tm.tmAveCharWidth + tm.tmOverhang;
	CHAR_HEIGHT = tm.tmHeight;

	SelectFont(hdc, old);

	ReleaseDC(hwnd, hdc);
}

static PyObject *
get_logfont(LOGFONT *lf)
{
	*lf = s_lfDefault;

	Py_RETURN_NONE;
}

void
gui_platform_free_font(GuiFont font)
{
	if (font)
		DeleteObject((HFONT)font);
}

void
gui_platform_set_font(GuiFont font)
{
	s_font = font;
}

static PyObject *
gui_platform_init_font()
{
	LOGFONT		lf;
	GuiFont		font = NOFONT;

	if (get_logfont(&lf))
		font = get_font_handle(&lf);
	if (font == NOFONT) {
		PyErr_Format(PyExc_RuntimeError, "Init Font Error");
		return NULL;
	}

	gui_platform_free_font(s_font);
	s_font = font;
	get_font_size(font);
	gui_platform_set_font(font);
	
	Py_RETURN_NONE;
}

static PyObject *
gui_shell_closed()
{
	Py_XDECREF(screen);
	Py_XDECREF(WINDOW_SET);
	Py_Exit(0);
	Py_RETURN_NONE;
}

static PyObject *
gui_resize_shell(int width, int height)
{

	SetWindowPos(s_textArea, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
	Py_RETURN_NONE;
}

static void
_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
	gui_resize_shell(cx, cy);
}

static void
_OnClose(HWND hwnd)
{
	gui_shell_closed();
}

static void
_OnDestroy(HWND hwnd)
{
	_OnClose(hwnd);
}

static void
_OnChar(HWND hwnd, UINT ch, int repeat)
{
	push_input(0, ch);
//	typebuf.tb_buf[typebuf.tb_len++] = (wchar_t)ch;
}

static void
_OnPaint(HWND hwnd)
{
	PAINTSTRUCT ps;

	BeginPaint(hwnd, &ps);

	gui_redraw(0, 0, 24, 79);
	//gui_redraw(ps.rcPaint.left, ps.rcPaint.top, 
	//		ps.rcPaint.right - ps.rcPaint.left + 1,
	//		ps.rcPaint.bottom - ps.rcPaint.top + 1);

	EndPaint(hwnd, &ps);
}

static void
_OnLeftDOWN(HWND hwnd)
{
}

static LRESULT CALLBACK
_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		HANDLE_MSG(hwnd, WM_CLOSE,		_OnClose);
		HANDLE_MSG(hwnd, WM_DESTROY,	_OnDestroy);
		HANDLE_MSG(hwnd, WM_SIZE,		_OnSize);
		HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnLeftDOWN);

		HANDLE_MSG(hwnd, WM_PAINT,		_OnPaint);

		case WM_CHAR:
			_OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
			return 0L;

		default:
			return DefWindowProcW(hwnd, msg, wParam, lParam);
	}

	return 1;
}

static LRESULT CALLBACK
_TextAreaWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnLeftDOWN);
		default:
			return DefWindowProcW(hwnd, msg, wParam, lParam);
	}

	return 1;
}

PyDoc_STRVAR(pyvim_gui_init_doc,
"gui init\n");


static PyObject *
gui_platform_init(PyObject *self)
{
	const char pyvim_wnd_class_name[] = VIM_CLASS;
	const WCHAR pyvim_wnd_class_name_w[] = VIM_CLASS_W;
	const char pyvim_text_area_class_name[] = "PyVimTextArea";
	WNDCLASS	wndclass;
	WNDCLASSW	wndclassw;

	s_hinst = GetModuleHandle(NULL);

	if (s_hwnd != NULL)
		Py_RETURN_NONE;

	s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));

	if (GetClassInfoW(s_hinst, pyvim_wnd_class_name_w, &wndclassw) == 0) {
		wndclassw.style = CS_DBLCLKS;
		wndclassw.lpfnWndProc = _WndProc;
		wndclassw.cbClsExtra = 0;
		wndclassw.cbWndExtra = 0;
		wndclassw.hInstance = s_hinst;
		wndclassw.hIcon = NULL;
		wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
		wndclassw.hbrBackground = s_brush;
		wndclassw.lpszMenuName = NULL;
		wndclassw.lpszClassName = pyvim_wnd_class_name_w;

		if (RegisterClassW(&wndclassw) == 0) {
			PyErr_Format(PyExc_RuntimeError, "MS Register Window Class \"%s\" Error", 
					pyvim_wnd_class_name);
			return NULL;
		}
	}

	s_hwnd = CreateWindow(
			pyvim_wnd_class_name, "PyVim MSWindows GUI",
			WS_OVERLAPPEDWINDOW,
			CW_USEDEFAULT, CW_USEDEFAULT, 100, 100,
			NULL, NULL, s_hinst, NULL);
	if (s_hwnd == NULL) {
		printf("Error code: %d\n", GetLastError());
		PyErr_Format(PyExc_RuntimeError, "MS Create Window \"%s\" Error",
				pyvim_wnd_class_name);
		return NULL;
	}

	if (GetClassInfo(s_hinst, pyvim_text_area_class_name, &wndclass) == 0) {
		wndclass.style = CS_OWNDC;
		wndclass.lpfnWndProc = _TextAreaWndProc;
		wndclass.cbClsExtra = 0;
		wndclass.cbWndExtra = 0;
		wndclass.hInstance = s_hinst;
		wndclass.hIcon = NULL;
		wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
		wndclass.hbrBackground = NULL;
		wndclass.lpszMenuName = NULL;
		wndclass.lpszClassName = pyvim_text_area_class_name;

		if (RegisterClass(&wndclass) == 0) {
			PyErr_Format(PyExc_RuntimeError, "MS Register Window Class \"%s\" Error", 
					pyvim_text_area_class_name);
			return NULL;
		}
	}

	s_textArea = CreateWindowEx(
			WS_EX_CLIENTEDGE,
			pyvim_text_area_class_name, "Vim text area",
			WS_CHILD | WS_VISIBLE,
			0, 0, 100, 100,
			s_hwnd, NULL, s_hinst, NULL);
	if (s_textArea == NULL) {
		PyErr_Format(PyExc_RuntimeError, "MS Create Window \"%s\" Error",
				pyvim_text_area_class_name);
		return NULL;
	}

	s_hdc = GetDC(s_textArea);
	DragAcceptFiles(s_hwnd, TRUE);

	ShowWindow(s_hwnd, SW_SHOWNORMAL);

	Py_RETURN_NONE;
}

void 
gui_platform_set_shellsize()
{
	int x, y;
	int width;
	int height;
	WINDOWPLACEMENT	wndpl;

	GetWindowPlacement(s_hwnd, &wndpl);

	x = wndpl.rcNormalPosition.left;
	y = wndpl.rcNormalPosition.top;

	width = Columns * CHAR_WIDTH 
		+ GetSystemMetrics(SM_CXFRAME) * 2 + 4;
	height = Rows * CHAR_HEIGHT + GetSystemMetrics(SM_CYFRAME) * 2
		+ GetSystemMetrics(SM_CYCAPTION);

	wndpl.rcNormalPosition.left = x;
	wndpl.rcNormalPosition.right = x + width;
	wndpl.rcNormalPosition.top = y;
	wndpl.rcNormalPosition.bottom = y + height;

	SetWindowPlacement(s_hwnd, &wndpl);
	SetActiveWindow(s_hwnd);
	SetFocus(s_hwnd);
}

static PyObject *
gui_draw_cursor_shape(int w, int h, int row, int col, Py_UNICODE c, 
		int isopaque, guicolor_T color)
{
	HBRUSH		hbr;
	RECT		rc;
	guicolor_T	fgcolor;

	if (isopaque) {
		rc.left = FILL_X(col);
		rc.top = FILL_Y(row) + CHAR_HEIGHT - h;
		rc.right = rc.left + w;
		rc.bottom = rc.top + h;

		hbr = CreateSolidBrush(color);
		FillRect(s_hdc, &rc, hbr);
		DeleteBrush(hbr);
	} else {
		fgcolor = -color;
		gui_mch_draw_string(row, col, &c, 1, fgcolor, color);
	}

	Py_RETURN_NONE;
}

static PyObject *
gui_draw_cursor(int show)
{
	Py_UNICODE	c;
	int			w, h, row, col, isopaque;

	if (!PvWindow_CursorInWindow(CURWIN))
		Py_RETURN_NONE;
	col = COL_BUF2SCR(CURWIN, WIN_WCOL(CURWIN));
	row = ROW_BUF2SCR(CURWIN, WIN_ROW(CURWIN));
	c = *PvScreen_Gets(screen, row, col, 1);

	if (show) {
		h = CHAR_HEIGHT;
		w = CHAR_WIDTH;
		isopaque = 0;
		if (CURMODE == NORMAL) {
			if (wcwidth(c) == 2)
				w = 2 * CHAR_WIDTH;
			if (c == ' ')
				isopaque = 1;
		} else if (CURMODE == INSERT) {
			w = 2;
			isopaque = 1;
		}

		return gui_draw_cursor_shape(w, h, row, col, c, isopaque, s_cursorcolor);
	} else {
		return gui_mch_draw_string(row, col, &c, 1, s_forecolor, s_backcolor);
	}
}

static PyObject *
pyvim_gui_init(PyObject *self)
{

	typebuf.tb_buf = PyMem_Malloc(265);
	typebuf.tb_buf_len = 265;
	typebuf.tb_len = 0;

	cmdbuf[0] = 0;

	screen = PyObject_CallFunction((PyObject *)&PvScreen_Type,
			"ii", Rows, Columns);
	if(!gui_platform_init_font())
		return NULL;

	if(!gui_platform_init(self))
		return NULL;

	gui_platform_set_shellsize();

	Py_RETURN_NONE;
}

typedef struct {
	int		modifiers;
	int		code;
} input_T;
#define INPUT_MAX_LEN	256

static input_T inputbuf[INPUT_MAX_LEN];
static int inputbuf_off = 0;
static int inputbuf_len = 0;

static void
push_input(int modifiers, int code)
{
	input_T entry;

	assert(inputbuf_len <= INPUT_MAX_LEN);

	entry.modifiers = modifiers;
	entry.code = code;
	/* the inputbuf is full, we will dispose the unuse unit*/
	if (inputbuf_len == INPUT_MAX_LEN) {
		if (inputbuf_off == 0) {
			memmove(inputbuf, inputbuf + 1, (inputbuf_len - 1) * sizeof(input_T));
			inputbuf[inputbuf_len - 1] = entry;
			return;
		}
		memmove(inputbuf, inputbuf + inputbuf_off, 
				(inputbuf_len - inputbuf_off) * sizeof(input_T));
		inputbuf_off = 0;
		inputbuf_len -= inputbuf_off;
		inputbuf[inputbuf_len - 1] = entry;
	} else {
		inputbuf[inputbuf_len++] = entry;
	}
}

static input_T
pop_input()
{
	input_T entry;
	assert(inputbuf_off < inputbuf_len);
	entry = inputbuf[inputbuf_off++];
	if (inputbuf_off == inputbuf_len)
		inputbuf_off = inputbuf_len = 0;
	return entry;
}

static int
input_isvalid()
{
	return inputbuf_off < inputbuf_len;
}

static PyObject *
_push_input(PyObject *self, PyObject *args)
{
	int modifiers, code;
	if (!PyArg_ParseTuple(args, "ii", &modifiers, &code))
		return NULL;
	push_input(modifiers, code);
	Py_RETURN_NONE;
}

static PyObject *
_pop_input(PyObject *self)
{
	input_T ret;
	ret = pop_input();
	return Py_BuildValue("(ii)", ret.modifiers, ret.code);
}

static PyObject *
trash_input(PyObject *self)
{
	inputbuf_len = inputbuf_off = 0;
	Py_RETURN_NONE;
}

static PyObject *
input_info(PyObject *self)
{
	return Py_BuildValue("(ii)", inputbuf_len, inputbuf_off);
}

/*
 * Get IM status.  When IM is on, return not 0.  Else return 0.
 */
    int
im_get_status()
{
    int		status = 0;
    HIMC	hImc;

    if (ImmGetContext && (hImc = ImmGetContext(s_hwnd)) != (HIMC)0) {
		status = ImmGetOpenStatus(hImc) ? 1 : 0;
		ImmReleaseContext(s_hwnd, hImc);
    }
    return status;
}

#define MOD_MASK_CONTROL	1
#define MOD_MASK_ALT		2
#define MOD_MASK_SHIFT		4
#define MOD_MASK_MENU		MOD_MASK_ALT

static void
process_message()
{
	MSG msg;
	int ret, vk;
	int modifiers = 0;

	Py_BEGIN_ALLOW_THREADS
	ret = GetMessage(&msg, NULL, 0, 0);
	Py_END_ALLOW_THREADS

	if (im_get_status()) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		return;
	}
    if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) {
		vk = (int)msg.wParam;
		if (vk != VK_CONTROL && vk != VK_MENU && vk != VK_SHIFT) {
			if (GetKeyState(VK_CONTROL) & 0x80000)
			   modifiers |= MOD_MASK_CONTROL;	
			if (GetKeyState(VK_MENU) & 0x80000)
			   modifiers |= MOD_MASK_ALT;	
			if (GetKeyState(VK_SHIFT) & 0x80000)
			   modifiers |= MOD_MASK_SHIFT;	

			/* Ctrl-[A-Z[\]] can translate control char (ascii 0-0x32) */
			if (modifiers == 0 || 
					modifiers == MOD_MASK_CONTROL ||
					modifiers == MOD_MASK_SHIFT) {
				MSG m;
				TranslateMessage(&msg);
				PeekMessage(&m, NULL, 0, 0, PM_NOREMOVE);
				if (m.message == WM_CHAR)
					DispatchMessage(&msg);
				else
					push_input(modifiers, vk);
			} else 
				push_input(modifiers, vk);
		}
	}
	DispatchMessage(&msg);
}

static input_T
vgetc()
{
	while(1) {
		process_message();
		if(input_isvalid())
			return pop_input();
	}
}

char *
input_str(input_T c, char *buf)
{
	int pos = 0;

	pos += sprintf(buf + pos, "<");
	if (c.modifiers & MOD_MASK_CONTROL)
		pos += sprintf(buf + pos, "C-");
	if (c.modifiers & MOD_MASK_ALT)
		pos += sprintf(buf + pos, "M-");
	if (c.modifiers & MOD_MASK_SHIFT)
		pos += sprintf(buf + pos, "S-");
	pos += sprintf(buf + pos, "%x>", c.code);
	return buf;
}

static PyObject *
edit()
{
	input_T c;
	char buf[20];

	WIN_MODE(CURWIN) = INSERT;
	gui_draw_cursor(1);
	while(1) {
		c = vgetc();
		gui_draw_cursor(0);
		printf("[INSERT]: %s\n", input_str(c, buf));
		printf("get char: %d, %x, %c\n", c.modifiers, c.code, c.code);
		if (c.modifiers == 0 && c.code == ESC) {
			WIN_MODE(CURWIN) = NORMAL;
			Py_RETURN_NONE;
		} else if (c.modifiers == 0) {
			if (c.code == '\r') 
				c.code = '\n';
			{
				PyObject *u = PyUnicode_FromUnicode(&((Py_UNICODE)c.code), 1);
				PvWindow_Insert(CURWIN, u);
				Py_XDECREF(u);
			}
			if (c.code == '\n')
				PvWindow_MoveTo(CURWIN, WIN_ROW(CURWIN)+1, 0);
			else
				PvWindow_MoveRight(CURWIN, 1);
		}
		PvScreen_Update(screen, 0, 0, 24, 79);
		gui_redraw(0, 0, 24, 79);
		gui_draw_cursor(1);
	}

	Py_RETURN_NONE;
}

static PyObject *
docmdline(Py_UNICODE c)
{
	WIN_MODE(CURWIN) = CMDLINE;

	Py_RETURN_NONE;
}

static PyObject *
normal_cmd()
{
	input_T c;
	char buf[20];
	c = vgetc();
	gui_draw_cursor(0);
	printf("[NORMAL]: %s\n", input_str(c, buf));
	printf("get char: %d, %x, %c\n", c.modifiers, c.code, c.code);
	if (c.modifiers == 0 && c.code == 'i') {
		gui_draw_cursor(0);
		edit();
	} else if (c.modifiers == 0 && c.code == ':')
		docmdline(':');
	else if (c.modifiers == 0 && c.code == 'h')
		PvWindow_MoveLeft(CURWIN, 1);
	else if (c.modifiers == 0 && c.code == 'j')
		PvWindow_MoveDown(CURWIN, 1);
	else if (c.modifiers == 0 && c.code == 'k')
		PvWindow_MoveUp(CURWIN, 1);
	else if (c.modifiers == 0 && c.code == 'l')
		PvWindow_MoveRight(CURWIN, 1);

	//PvScreen_Update(screen, 0, 0, 24, 79);
	//gui_redraw(0, 0, 24, 79);
	gui_draw_cursor(1);

	Py_RETURN_NONE;
}

static PyObject *
pyvim_gui_loop(PyObject *self)
{
	while(1) {
		printf("loop\n");
		normal_cmd();
	}
#if 0
	MSG msg;
	int r;

	while (1) {
		Py_BEGIN_ALLOW_THREADS
		r = GetMessage(&msg, NULL, 0, 0);
		Py_END_ALLOW_THREADS
		if (!r) 
			break;
		TranslateMessage(&msg);
		DispatchMessage(&msg);

		parse_input();
	}
#endif

	Py_RETURN_NONE;
}

static PyObject *
gui_mch_draw_string(long row, long col, wchar_t *text, int len, 
		guicolor_T fgcolor, guicolor_T bgcolor)
{
	long		x, y;
	const RECT	*pcliprect = NULL;
	UINT		foptions = 0;

	if (len == 0) 
		Py_RETURN_NONE;
	if (len < 0) 
		len = lstrlenW(text);

	SelectFont(s_hdc, s_font);

	y = row * CHAR_HEIGHT;
	x = col * CHAR_WIDTH;

	SetBkColor(s_hdc, bgcolor);
	SetTextColor(s_hdc, fgcolor);
	ExtTextOutW(s_hdc, x, y, 
			foptions, pcliprect, text, len, NULL);

	Py_RETURN_NONE;
}

PyDoc_STRVAR(pyvim_text_doc,
"text(row, col, text), draw text at (row, col)\n");

static PyObject *
pyvim_text(PyObject *self, PyObject *args)
{
	int row, col;
	PyObject *text;
	if (!PyArg_ParseTuple(args, "iiO", &row, &col, &text))
		return NULL;

	return gui_mch_draw_string(row, col, PyUnicode_AS_UNICODE(text), 
			PyUnicode_GetSize(text), s_forecolor, s_backcolor);
}

static PyObject *
gui_redraw(int row1, int col1, int row2, int col2)
{
	int		row, len;
	wchar_t *text;

	len = col2 - col1 + 1;

	/* draw screen block */
	for (row = row1; row <= row2; row++) {
		text = PvScreen_Gets(screen, row, col1, len);
		if (text == NULL) {
			PyErr_Print();
			break;
		}
		//printf("draw string: %d, %x, %d, %d\n", row, text[0], col1, len);
		gui_mch_draw_string(row, col1,  text, len, s_forecolor, s_backcolor);
		PyMem_Free(text);
	}

#if 0
	{
		gui_mch_draw_string(SCR_ROWS(screen)-1, 0, L":cmdbuf", -1);//cmdbuf, lstrlenW(cmdbuf)); 
	}
#endif

	Py_RETURN_NONE;
}

PyDoc_STRVAR(pyvim_redraw_doc,
"redraw(r1, c1, r2, c2), redraw the specified area\n");

static PyObject *
pyvim_redraw(PyObject *self, PyObject *args)
{
	int row1, col1, row2, col2;

	if (!PyArg_ParseTuple(args, "iiii", &row1, &col1, &row2, &col2))
		return NULL;

	return gui_redraw(row1, col1, row2, col2);
}

PyDoc_STRVAR(pyvim_getscreen_doc,
"getscreen(), return the screen object\n");

static PyObject *
pyvim_get_screen(PyObject *self)
{
	if (screen == NULL)
		return Py_None;
	else
		return screen;
}

static PyObject *
pyvim_test(PyObject *self)
{
	PyObject *u = PyUnicode_InternFromString("abcde");
	PyObject *u1 =  PyUnicode_InternFromString("e");
	PyObject *u2;

	printf("%d,%d\n", u->ob_refcnt, u1->ob_refcnt);
	u2 = PyUnicode_Concat(u, u1);
	printf("%d,%d\n", u->ob_refcnt, u1->ob_refcnt);

	myprint(PyUnicode_Splitlines(PyUnicode_FromString("a\nb\nc\nd"), 1));
	myprint(PyUnicode_Splitlines(PyUnicode_FromString("\n"), 1));

/*
	SelectFont(s_hdc, gui.currFont);
	for (i = 0; i < 0xffff; i++) {
		GetTextExtentPoint32W(s_hdc, &i, 1, &size);
		printf("0x%x: %d, %d\n", i, size.cx, size.cy);
	}
*/
	Py_RETURN_NONE;
}

PyDoc_STRVAR(unicode_insert_doc,
"insert(str1, str2, pos), insert str2 to str1 at pos\n");

static PyObject *
unicode_insert(PyObject *self, PyObject *args)
{
	PyObject *str1, *str2;
	Py_ssize_t pos;
	if (!PyArg_ParseTuple(args, "OiO", &str1, &pos, &str2))
		return NULL;

	return PyUnicode_Insert(str1, pos, str2);
}

PyDoc_STRVAR(_seg_union_doc,
"get the union of two segments, [start1, end1], [start2, end2]\n"
"return value is a tuple (flag, start, end) \n"
"flag value -1 means two segments is not intersected\n"
"flag value 0 means [start2, end2] is included in [*start1, *end1], \n"
"start1 and end1 is not changed.\n"
"flag value 1 means start1 or end1 is changed\n");

static PyObject *
_seg_union(PyObject *self, PyObject *args)
{
	int s1, e1, s2, e2;
	int ret;

	if (!PyArg_ParseTuple(args, "iiii", &s1, &e1, &s2, &e2))
		return NULL;
	ret = seg_union(&s1, &e1, s2, e2);
	return Py_BuildValue("(iii)", ret, s1, e1);
}

static struct PyMethodDef pyvim_methods[] = {
	{"gui_init", (PyCFunction)pyvim_gui_init, METH_NOARGS,
		pyvim_gui_init_doc},
	{"loop", (PyCFunction)pyvim_gui_loop, METH_NOARGS,
		pyvim_gui_init_doc},
	{"test", (PyCFunction)pyvim_test, METH_NOARGS,
		pyvim_gui_init_doc},
	{"text", (PyCFunction)pyvim_text, METH_VARARGS,
		pyvim_text_doc},
	{"redraw", (PyCFunction)pyvim_redraw, METH_VARARGS,
		pyvim_redraw_doc},
	{"getscreen", (PyCFunction)pyvim_get_screen, METH_NOARGS,
		pyvim_getscreen_doc},
	{"insert", (PyCFunction)unicode_insert, METH_VARARGS,
		unicode_insert_doc},
	{"seg_union", (PyCFunction)_seg_union, METH_VARARGS,
		_seg_union_doc},
	{"pop_input", (PyCFunction)_pop_input, METH_NOARGS,
		""},
	{"push_input", (PyCFunction)_push_input, METH_VARARGS,
		""},
	{"input_info", (PyCFunction)input_info, METH_NOARGS,
		""},
	{"trash_input", (PyCFunction)trash_input, METH_NOARGS,
		""},
	{NULL, NULL}
};

static PyModuleDef pyvim_module = {
	PyModuleDef_HEAD_INIT,
	"pyvim",
	pyvim_doc,
	-1,
	pyvim_methods
};

PyMODINIT_FUNC
PyInit_pyvim(void)
{
	PyObject *m;

	if (PyType_Ready(&PvBuffer_Type) < 0)
		return NULL;

	if (PyType_Ready(&PvScreenBuffer_Type) < 0)
		return NULL;

	if (PyType_Ready(&PvWindow_Type) < 0)
		return NULL;

	if (PyType_Ready(&PvFrame_Type) < 0)
		return NULL;

	if (PyType_Ready(&PvScreen_Type) < 0)
		return NULL;

	if (PyType_Ready(&PvSegments_Type) < 0)
		return NULL;

	m = PyModule_Create(&pyvim_module);
	if (m == NULL)
		return NULL;

	Py_INCREF(&PvBuffer_Type);
	PyModule_AddObject(m, "buffer", (PyObject *)&PvBuffer_Type);
	Py_INCREF(&PvScreenBuffer_Type);
	PyModule_AddObject(m, "screen_buffer", (PyObject *)&PvScreenBuffer_Type);
	Py_INCREF(&PvScreen_Type);
	PyModule_AddObject(m, "screen", (PyObject *)&PvScreen_Type);
	Py_INCREF(&PvWindow_Type);
	PyModule_AddObject(m, "window", (PyObject *)&PvWindow_Type);
	Py_INCREF(&PvFrame_Type);
	PyModule_AddObject(m, "frame", (PyObject *)&PvFrame_Type);
	Py_INCREF(&PvSegments_Type);
	PyModule_AddObject(m, "segments", (PyObject *)&PvSegments_Type);

	PyModule_AddIntMacro(m, FR_LEAF);
	PyModule_AddIntMacro(m, FR_COL);
	PyModule_AddIntMacro(m, FR_ROW);
	PyModule_AddIntMacro(m, FSP_HOR);
	PyModule_AddIntMacro(m, FSP_VER);

	return m;
}
